LBalloonTracker - a class for implementing balloon help.
What is it?
LBalloonTracker is a class that implements Balloon Help throughout an entire PowerPlant view hierarchy. Typically used as a mix-in class with LWindow or LDialog, it displays help content corresponding to the pane under the pointer as well as the pane's state. Help content is supplied in the form of a resource similar to the 'hdlg' resource, but with additional information in it. This scheme minimises the number of points of intervention, i.e., the number of places where you have to add stuff to your code in order to implement Balloon Help.
The LBalloonTracker package contains these files:
LBalloonTracker.doc -- this documentation file
LBalloonTracker.cp -- source file
LBalloonTracker.h -- header file
LBalloonTracker.r -- Rez template file for balloon help content
LBalloonTracker.tmpl -- Resorcerer template file for balloon help content
How do I use it?
Unlike most other Balloon Help implementations for PowerPlant, LBalloonTracker is typically used as a mix-in class at the window level. That is, programmers derive a new window class from both LBalloonTracker and either LWindow or LDialog. Only a few functions need to be overridden. Here's an example:
class MyWindow : public LWindow, public LBalloonTracker
The override of AdjustCursor is where control is transferred to the balloon tracking code (via the call to TrackBalloons). The other two overrides ensure that any displayed balloons are removed when a window is hidden or becomes inactive.
In order to complete the picture, your window's creation code needs to tell the window which balloon help resource to look for. You can do this easily:
You can also set the balloon help resource ID in the call to LBalloonTracker's constructor. For example:
MyWindow::MyWindow(LStream* inStream)
: LWindow(inStream), LBalloonTracker(1234)
{
}
How does it work?
LBalloonTracker, as its name implies, tracks the pointer across an entire view hierarchy. Inside the call to TrackBalloons, it performs a depth-first search for panes that are underneath the cursor. If it finds one, it extracts the pane's ID and uses it as a search key in the tracker's balloon help resource. If balloon help is found for that pane, it is displayed; if not, the tracker searches again using the pane's superview's ID. This process is repeated until balloon help is found or we hit the top of the pane hierarchy. If no help content is found, any currently displayed help balloon is removed.
How do I define help content?
All of the balloon help resources described in Inside Macintosh ('hdlg', 'hwin', etc.) are index-based. That is, they all assume that visual elements on the screen are identified linearly, in the way that Dialog Manager items are. Unfortunately, this doesn't work very well with PowerPlant's hierarchical display system (how do you map an index onto a tree?). For this reason, LBalloonTracker uses its own custom resource type for describing help content. This resource, of type 'PPbl', is very similar to the Help Manager's 'hdlg' resource, except that every item in the resource consists of a key-value pair. The key is a pane's ID, and the value is its help content. For example:
#include "LBalloonTracker.r"
resource 'PPbl' (128, purgeable) {
kCurrentVersion, hmDefaultOptions, 0, 0,
{
// pane #1
1000, // pane ID
HMStringItem {
"this is help for the pane 1000's enabled state",
"this is help for the pane 1000's disabled state",
"this is help for the pane 1000's checked state",
"this is help for the pane 1000's other state"
},
// pane #2
1001, // pane ID
HMStringItem {
"this is help for the pane 1001's enabled state",
"this is help for the pane 1001's disabled state",
"this is help for the pane 1001's checked state",
"this is help for the pane 1001's other state"
},
// etc.
}
};
The help content can be specified in any of the ways defined by the 'hdlg' resource: by embedded Pascal string (HMStringItem), by 'PICT' resource ID (HMPictItem), by 'STR#' resource ID and index (HMStringResItem), by 'TEXT' resource ID (HMTEResItem), and by 'STR ' resource ID (HMSTRResItem).
For each pane listed in the 'PPbl' resource, there are four possible help messages: "enabled", "disabled", "checked", and "other". These messages correspond to various states of the pane. Note that LBalloonTracker will automatically detect state changes in panes, so the programmer doesn't have to do anything.
The possible pane states are determined as follows. If the pane is disabled (as determined by a call to IsEnabled()), its state is "disabled". If the pane is enabled and it does not derive from LControl, its state is "enabled". If the pane is enabled and it does derive from LControl, we get the pane's value. If the value is 0, the pane's state is "enabled"; if it's 1, the pane's state is "checked"; if it's anything else, the pane's state is "other". This behaviour mimics the Help Manager's use of 'hdlg' resources.
Note: In order to take advantage of the behaviour for LControls, the "Enable RTTI" option has to be turned on in the "C/C++ Language" panel of the Project Settings dialog.
There are two files that describe the format of LBalloonTracker's help resource: "LBalloonTracker.r" contains a Rez type definition, and "LBalloonTracker.tmpl" contains a Resorcerer template. There is no ResEdit template.
Known problems
PowerPlant's use of LDefaultOutline and LFocusBox causes problems with the balloon-tracking mechanism. This is because the outline and focus box aren't actually part of the panes they ostensibly enhance. PowerPlant's own mouse-tracking functions (FindDeepSubPaneContaining) see the outline and focus box as being "on top of" these panes.
I have no intention of "fixing" this, since in my own apps I use custom button & scroller classes that draw their own outline or focus box directly.